//! 配置加载集成测试
//!
//! 测试 load_config 函数的各种路径类型和边界条件
//! Story 3.4: 实现代码指定配置路径

#![allow(clippy::unnecessary_unwrap)]

use log4r::config::{load_config, Log4rConfig};
use log4r::Level;
use std::path::{Path, PathBuf};
use tempfile::tempdir;

// ============================================================================
// AC-3.4.1: load_config 支持多种路径类型
// ============================================================================

/// [P0] 测试 load_config 接受 &str 路径
#[test]
fn test_load_config_accepts_str() {
    // GIVEN: 有效的配置文件
    let temp = tempdir().expect("创建临时目录失败");
    let config_path = temp.path().join("test.toml");
    std::fs::write(
        &config_path,
        r#"
[log4r]
level = "debug"
app_name = "str-test"
"#,
    )
    .expect("写入文件失败");

    // WHEN: 使用 &str 路径加载
    let path_str = config_path.to_str().expect("路径转换失败");
    let config = load_config(path_str).expect("加载配置应成功");

    // THEN: 配置正确解析
    assert_eq!(config.level, Level::Debug);
    assert_eq!(config.app_name, "str-test");
}

/// [P0] 测试 load_config 接受 String 路径
#[test]
fn test_load_config_accepts_string() {
    // GIVEN: 有效的配置文件
    let temp = tempdir().expect("创建临时目录失败");
    let config_path = temp.path().join("test.toml");
    std::fs::write(
        &config_path,
        r#"
[log4r]
level = "warn"
app_name = "string-test"
"#,
    )
    .expect("写入文件失败");

    // WHEN: 使用 String 路径加载
    let path_string = config_path.to_string_lossy().to_string();
    let config = load_config(&path_string).expect("加载配置应成功");

    // THEN: 配置正确解析
    assert_eq!(config.level, Level::Warn);
    assert_eq!(config.app_name, "string-test");
}

/// [P0] 测试 load_config 接受 &Path 路径
#[test]
fn test_load_config_accepts_path_ref() {
    // GIVEN: 有效的配置文件
    let temp = tempdir().expect("创建临时目录失败");
    let config_path = temp.path().join("test.toml");
    std::fs::write(
        &config_path,
        r#"
[log4r]
level = "error"
app_name = "path-ref-test"
"#,
    )
    .expect("写入文件失败");

    // WHEN: 使用 &Path 路径加载
    let path_ref: &Path = config_path.as_path();
    let config = load_config(path_ref).expect("加载配置应成功");

    // THEN: 配置正确解析
    assert_eq!(config.level, Level::Error);
    assert_eq!(config.app_name, "path-ref-test");
}

/// [P0] 测试 load_config 接受 PathBuf 路径
#[test]
fn test_load_config_accepts_pathbuf() {
    // GIVEN: 有效的配置文件
    let temp = tempdir().expect("创建临时目录失败");
    let config_path: PathBuf = temp.path().join("test.toml");
    std::fs::write(
        &config_path,
        r#"
[log4r]
level = "trace"
app_name = "pathbuf-test"
"#,
    )
    .expect("写入文件失败");

    // WHEN: 使用 PathBuf 路径加载
    let config = load_config(&config_path).expect("加载配置应成功");

    // THEN: 配置正确解析
    assert_eq!(config.level, Level::Trace);
    assert_eq!(config.app_name, "pathbuf-test");
}

// ============================================================================
// AC-3.4.2: 支持相对路径和绝对路径
// ============================================================================

/// [P1] 测试 load_config 支持绝对路径
#[test]
fn test_load_config_absolute_path() {
    // GIVEN: 绝对路径的配置文件
    let temp = tempdir().expect("创建临时目录失败");
    let config_path = temp.path().join("absolute.toml");
    std::fs::write(
        &config_path,
        r#"
[log4r]
level = "info"
app_name = "absolute-path-test"
"#,
    )
    .expect("写入文件失败");

    // WHEN: 使用绝对路径加载
    let absolute_path = config_path.canonicalize().expect("获取绝对路径失败");
    let config = load_config(&absolute_path).expect("加载配置应成功");

    // THEN: 配置正确解析
    assert_eq!(config.level, Level::Info);
    assert_eq!(config.app_name, "absolute-path-test");
}

// ============================================================================
// AC-3.4.3: 文件不存在时返回 IoError
// ============================================================================

/// [P0] 测试 load_config 文件不存在返回 IoError
#[test]
fn test_load_config_file_not_found() {
    // GIVEN: 不存在的文件路径
    let nonexistent_path = "this_file_does_not_exist_12345.toml";

    // WHEN: 尝试加载
    let result = load_config(nonexistent_path);

    // THEN: 返回 IoError
    assert!(result.is_err());
    let err = result.unwrap_err();
    assert!(err.to_string().contains("IO"), "错误应为 IO 错误: {}", err);
}

/// [P1] 测试 load_config 目录路径返回 IoError
#[test]
fn test_load_config_directory_path() {
    // GIVEN: 目录路径而非文件
    let temp = tempdir().expect("创建临时目录失败");

    // WHEN: 尝试加载目录
    let result = load_config(temp.path());

    // THEN: 返回 IoError
    assert!(result.is_err());
}

// ============================================================================
// AC-3.4.4: TOML 解析失败返回 ConfigError
// ============================================================================

/// [P0] 测试 load_config 无效 TOML 返回 ConfigError
#[test]
fn test_load_config_invalid_toml() {
    // GIVEN: 无效的 TOML 文件
    let temp = tempdir().expect("创建临时目录失败");
    let config_path = temp.path().join("invalid.toml");
    std::fs::write(&config_path, "[log4r\nlevel = ").expect("写入文件失败");

    // WHEN: 尝试加载
    let result = load_config(&config_path);

    // THEN: 返回 ConfigError
    assert!(result.is_err());
    let err = result.unwrap_err();
    assert!(
        err.to_string().contains("配置"),
        "错误应为配置错误: {}",
        err
    );
}

/// [P1] 测试 load_config 无效级别返回 ConfigError
#[test]
fn test_load_config_invalid_level() {
    // GIVEN: 包含无效级别的配置文件
    let temp = tempdir().expect("创建临时目录失败");
    let config_path = temp.path().join("invalid_level.toml");
    std::fs::write(
        &config_path,
        r#"
[log4r]
level = "not_a_valid_level"
"#,
    )
    .expect("写入文件失败");

    // WHEN: 尝试加载
    let result = load_config(&config_path);

    // THEN: 返回 ConfigError
    assert!(result.is_err());
}

// ============================================================================
// AC-3.4.5: 成功加载返回 Log4rConfig
// ============================================================================

/// [P0] 测试 load_config 返回类型为 Result<Log4rConfig, Log4rError>
#[test]
fn test_load_config_return_type() {
    // GIVEN: 有效的配置文件
    let temp = tempdir().expect("创建临时目录失败");
    let config_path = temp.path().join("valid.toml");
    std::fs::write(
        &config_path,
        r#"
[log4r]
level = "info"
"#,
    )
    .expect("写入文件失败");

    // WHEN: 加载配置
    let result: Result<Log4rConfig, log4r::Log4rError> = load_config(&config_path);

    // THEN: 返回 Ok(Log4rConfig)
    assert!(result.is_ok());
    let config = result.unwrap();
    assert_eq!(config.level, Level::Info);
}

// ============================================================================
// 边界条件测试
// ============================================================================

/// [P1] 测试 load_config 空文件返回默认配置
#[test]
fn test_load_config_empty_file() {
    // GIVEN: 空配置文件
    let temp = tempdir().expect("创建临时目录失败");
    let config_path = temp.path().join("empty.toml");
    std::fs::write(&config_path, "").expect("写入文件失败");

    // WHEN: 加载配置
    let config = load_config(&config_path).expect("空文件应返回默认配置");

    // THEN: 返回默认值
    assert_eq!(config.level, Level::Info);
    assert_eq!(config.app_name, "log4r");
}

/// [P1] 测试 load_config 仅注释文件返回默认配置
#[test]
fn test_load_config_comments_only_file() {
    // GIVEN: 仅包含注释的配置文件
    let temp = tempdir().expect("创建临时目录失败");
    let config_path = temp.path().join("comments.toml");
    std::fs::write(
        &config_path,
        r#"
# 这是注释
# 另一行注释

# 更多注释
"#,
    )
    .expect("写入文件失败");

    // WHEN: 加载配置
    let config = load_config(&config_path).expect("仅注释文件应返回默认配置");

    // THEN: 返回默认值
    assert_eq!(config.level, Level::Info);
}

/// [P2] 测试 load_config 大文件处理
#[test]
fn test_load_config_large_file() {
    // GIVEN: 包含大量模块配置的文件
    let temp = tempdir().expect("创建临时目录失败");
    let config_path = temp.path().join("large.toml");

    let mut content = String::from("[log4r]\nlevel = \"debug\"\n\n[log4r.modules]\n");
    for i in 0..100 {
        content.push_str(&format!("\"module_{}\" = \"trace\"\n", i));
    }
    std::fs::write(&config_path, &content).expect("写入文件失败");

    // WHEN: 加载配置
    let config = load_config(&config_path).expect("大文件应成功加载");

    // THEN: 配置正确解析
    assert_eq!(config.level, Level::Debug);
    assert_eq!(config.modules.len(), 100);
}

/// [P2] 测试 load_config 包含 UTF-8 BOM 的文件
#[test]
fn test_load_config_utf8_bom() {
    // GIVEN: 包含 UTF-8 BOM 的配置文件
    let temp = tempdir().expect("创建临时目录失败");
    let config_path = temp.path().join("bom.toml");

    // UTF-8 BOM: EF BB BF
    let mut content = vec![0xEF, 0xBB, 0xBF];
    content.extend_from_slice(
        r#"[log4r]
level = "warn"
app_name = "bom-test"
"#
        .as_bytes(),
    );
    std::fs::write(&config_path, &content).expect("写入文件失败");

    // WHEN: 加载配置
    // 注意：toml crate 可能不支持 BOM，这个测试验证行为
    let result = load_config(&config_path);

    // THEN: 根据实际行为断言（可能成功或失败）
    // 如果 toml 不支持 BOM，这里会失败，这是预期行为
    if result.is_ok() {
        let config = result.unwrap();
        assert_eq!(config.app_name, "bom-test");
    }
    // 如果失败，也是可接受的行为
}

/// [P2] 测试 load_config 中文路径
#[test]
fn test_load_config_unicode_path() {
    // GIVEN: 包含中文的路径
    let temp = tempdir().expect("创建临时目录失败");
    let config_path = temp.path().join("配置文件.toml");
    std::fs::write(
        &config_path,
        r#"
[log4r]
level = "info"
app_name = "中文路径测试"
"#,
    )
    .expect("写入文件失败");

    // WHEN: 加载配置
    let config = load_config(&config_path).expect("中文路径应成功加载");

    // THEN: 配置正确解析
    assert_eq!(config.app_name, "中文路径测试");
}
